/*
 * Copyright (c) 2015-2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *  ======== bleeaconTask.c ========
 */
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Error.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Clock.h>

/* TI-RTOS Header files */
#include <ti/drivers/PIN.h>

/* Board Header files */
#include "Board.h"

// DriverLib
#include <ti/drivers/rf/RF.h>

#include "smartrf_settings/smartrf_settings_ble.h"

/***** Defines *****/
#define BLE_TASK_STACK_SIZE 1024
#define BLE_TASK_PRIORITY   3

#define BLE_BEACON_PERIOD_MS    1000

#define BLE_PRI_IEEE_ADDR_LOCATION   0x500012E8
#define BLE_SEC_IEEE_ADDR_LOCATION   0x500012D0

#define BLE_RF_EVENT_MASK  ( RF_EventLastCmdDone | RF_EventCmdError | \
             RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled )

#define BLE_TX      Board_PIN_LED2
#define BLE_SUCCESS CC1350_LAUNCHXL_DIO21
#define BLE_ABORT   IOID_9
#define BLE_FAIL    CC1350_LAUNCHXL_DIO22

/// \brief Structure for the BLE Advertisement Packet
typedef struct
{
    uint8_t *deviceAddress;       ///Device Address used used in the
                                  ///rfc_CMD_BLE_ADV_NC_t Rf command
    uint8_t length;               ///Advertisement packet length
    uint8_t *pAdvData;            ///Advertisement data
} beaconFrame_t;

/***** Variable declarations *****/
static Task_Params bleBeaconTaskParams;
Task_Struct bleBeaconTask;    /* not static so you can see in ROV */
static uint8_t bleBeaconTaskStack[BLE_TASK_STACK_SIZE];

/* The RX Output struct contains statistics about the RX operation of the radio */
PIN_Handle pinHandle;

/// \brief advertisement interval array in 100us units, optimised for iOS.
///        iOS guidlines state 152.5ms 211.25ms 318.75ms 417.5ms 546.25ms
///        760ms 852.5ms 1022.5ms and 1285ms. In this application we can
///        only advertise for 1s as the sub1G packet rate can be as high
///        as every 1s.
uint32_t SimpleBeacon_AdvertisementIntervals[] = {225, 500, 800, 587, 1075, 988, 1287, 2137, 925};

/* proprietary advertisement packet */
static uint8_t localNameAdvertisement[] = {
        0x02, //Length of this Data section
        0x01, //<<Flags>>
        0x02, //LE General Discoverable Mode
        0x18, //Length of this Data section
        0x09, //<<Complete local name>>
        'C', 'C', '1', '3', '5', '0', ' ',
        'L', 'a', 'u', 'n', 'c', 'h',
        'p', 'a', 'd', ' ', '-', ' ', '0', 'x', '0', '0'
        };

/* proprietary advertisement packet */
static uint8_t propAdvertisement[] = {
        0x02, //Length of this section
        0x01, //<<Flags>>
        0x02, //LE General Discoverable Mode
        0x06, //Length of this section
        0xff, //<<Manufacturer Specific Data>>
        0x0d,
        0x00,
        0x03,
        0x00,
        0x00}; //BTN state

beaconFrame_t propAdvFrame;
beaconFrame_t localNameAdvFrame;

static uint8_t bleMacAddr[6];

static RF_Object bleRfObject;
static RF_Handle bleRfHandle;

/***** Function definitions *****/
static void sendBeacon(void);
void getIeeeAddr(uint8_t *ieeeAddr);

static void bleBeconRxFnx(UArg arg0, UArg arg1)
{

    getIeeeAddr(bleMacAddr);

    propAdvFrame.deviceAddress = bleMacAddr;
    propAdvFrame.length = sizeof(propAdvertisement);
    propAdvFrame.pAdvData = propAdvertisement;

    //convert nodeAddress to Ascii hex
    localNameAdvFrame.deviceAddress = bleMacAddr;
    localNameAdvFrame.length = sizeof(localNameAdvertisement);
    localNameAdvFrame.pAdvData = localNameAdvertisement;

    RF_pModeBle->rfMode = RF_MODE_MULTIPLE;

    bleRfHandle = RF_open(&bleRfObject, RF_pModeBle,
                           (RF_RadioSetup*)RF_ble_pCmdRadioSetup, NULL);


    /* Enter main task loop */
    while (1)
    {
        Task_sleep(BLE_BEACON_PERIOD_MS * 500 / Clock_tickPeriod);

        sendBeacon();
    }
}

void bleBeaconTask_init(PIN_Handle ledPinHandle) {
    pinHandle = ledPinHandle;

    Task_Params_init(&bleBeaconTaskParams);
    bleBeaconTaskParams.stackSize = BLE_TASK_STACK_SIZE;
    bleBeaconTaskParams.priority = BLE_TASK_PRIORITY;
    bleBeaconTaskParams.stack = &bleBeaconTaskStack;
    bleBeaconTaskParams.arg0 = (UInt)1000000;

    Task_construct(&bleBeaconTask, bleBeconRxFnx, &bleBeaconTaskParams, NULL);
}

//*****************************************************************************
//
//! \brief Send a beacon
//!
//! This transmits a beacon.
//!
//! \param beaconFrame Beacon to be Tx'ed
//! \param type The frame type to be transmitted
//! \param numTxPerChan Number of transmits per channel, if 0 use default
//! \param chanMask channel mask of channels to Tx on
//!
//! \return SimpleBeacon_Status
//!
//*****************************************************************************
static void sendBeacon(void)
{
    RF_EventMask result;
    uint32_t txCnt, numTxPerChan;
    uint32_t chIdx;
    rfc_bleAdvOutput_t advStats = {0};
    RF_ScheduleCmdParams bleSchParams;
    RF_CmdHandle cmdHdl;

    RF_ble_pCmdBleAdvNc->startTrigger.triggerType = TRIG_NOW;
    RF_ble_pCmdBleAdvNc->startTrigger.pastTrig = 0;
    RF_ble_pCmdBleAdvNc->startTime = 0;

    bleSchParams.priority = RF_PriorityHigh;
    //schParams.endTime = ?

    RF_ble_pCmdBleAdvNc->pParams->advLen = localNameAdvFrame.length;

    RF_ble_pCmdBleAdvNc->pParams->pAdvData = (uint8_t*) localNameAdvFrame.pAdvData;
    RF_ble_pCmdBleAdvNc->pParams->pDeviceAddress = (uint16_t*) localNameAdvFrame.deviceAddress;


    //only useful for debug
    RF_ble_pCmdBleAdvNc->pOutput = &advStats;

    result = RF_EventLastCmdDone;

    numTxPerChan = sizeof(SimpleBeacon_AdvertisementIntervals) / sizeof(uint32_t);

    //transmit BLE adv on adv channels for number of times specified, or
    //until error
    for (chIdx = 37; ((chIdx < 40) && (result & RF_EventLastCmdDone));
            chIdx++)
    {
        for (txCnt = 0;
            ((txCnt < numTxPerChan) &&
            (result == RF_EventLastCmdDone));
            txCnt++)
        {
            RF_ble_pCmdBleAdvNc->channel = chIdx;
            RF_ble_pCmdBleAdvNc->whitening.init = 0x40 + chIdx;

            PIN_setOutputValue(pinHandle, BLE_FAIL, 0);
            PIN_setOutputValue(pinHandle, BLE_ABORT, 0);
            PIN_setOutputValue(pinHandle, BLE_SUCCESS, 0);

            PIN_setOutputValue(pinHandle, BLE_TX, 1);
            cmdHdl = RF_scheduleCmd(bleRfHandle, (RF_Op*)RF_ble_pCmdBleAdvNc, &bleSchParams, 0, 0);
            result = RF_pendCmd(bleRfHandle, cmdHdl, BLE_RF_EVENT_MASK);
            PIN_setOutputValue(pinHandle, BLE_TX, 0);

            if(result == RF_EventCmdError)
            {
                PIN_setOutputValue(pinHandle, BLE_FAIL, 1);
            }
            else if (result & ( RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled))
            {
                PIN_setOutputValue(pinHandle, BLE_ABORT, 1);
            }
            else
            {
                PIN_setOutputValue(pinHandle, BLE_SUCCESS, 1);
            }

            //sleep on all but last advertisement
            if (txCnt+1 < numTxPerChan)
            {
               Task_sleep(SimpleBeacon_AdvertisementIntervals[txCnt]);
            }
        }
    }
}

void getIeeeAddr(uint8_t *ieeeAddr)
{
    if (ieeeAddr != NULL)
    {
        int i;

        //Reading from primary IEEE location...
        uint8_t *location = (uint8_t *)BLE_PRI_IEEE_ADDR_LOCATION;

        /*
         * ...unless we can find a byte != 0xFF in secondary
         *
         * Intentionally checking all 6 bytes here instead of len, because we
         * are checking validity of the entire IEEE address irrespective of the
         * actual number of bytes the caller wants to copy over.
         */
        for (i = 0; i < 6; i++) {
            if (((uint8_t *)BLE_SEC_IEEE_ADDR_LOCATION)[i] != 0xFF) {
                //A byte in the secondary location is not 0xFF. Use the
                //secondary
                location = (uint8_t *)BLE_SEC_IEEE_ADDR_LOCATION;
                break;
            }
        }

        //inverting byte order
       for (i = 0; i < 6; i++) {
           ieeeAddr[i] = location[6 - 1 - i];
       }
    }
}
